
package com.hlkj.pay.app.merchant.ext.impl.dffpay.ext;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.type.TypeReference;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.constant.DffPayConstant;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.DffPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.DffpayAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.req.*;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.rsp.*;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.utils.DffPayBase64;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.utils.DffPaySignUtil;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.utils.HttpUtils;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 东方付 正扫 反扫 通用处理
 * 
 * @author HlpayTeam
 * @date 2024/10/11 16:22
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class DffPaySceneCommonPayOrder {

    /**
     * 东方付查询支付订单
     *
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderQueryContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        DffPayConfig dffPayConfig = getPayConfig(channelConfig, channelPayOrderQueryContext.getMerchantAppChannelDO());

        PayOrderDO payOrderDO = channelPayOrderQueryContext.getPayOrderDO();
        try {

            OrderQueryReq orderQueryReq = new OrderQueryReq();
            orderQueryReq.setOrderId(payOrderDO.getInsOrderNo());
            String bodyJson = JsonUtils.toJsonString(orderQueryReq);
            String body = Base64.getEncoder().encodeToString(bodyJson.getBytes(StandardCharsets.UTF_8));
            HttpRequest request = createRequest(body, dffPayConfig);

            log.info("东方付订单查询req参数:{}", JsonUtils.toJsonString(request));
            String url = domain(channelPayOrderQueryContext.isTest()) + DffPayConstant.TRADE_QUERY;
            String response = HttpUtils.post(url, request);
            log.info("东方付订单查询返回参数:{}", response);

            // 响应报文验签
            HttpResponse ordeRsp = JsonUtils.parseObject(response, HttpResponse.class);
            if ("200".equals(ordeRsp.getCode())) {
                ResponseBody<OrderQueryRsp> rspBody = JsonUtils.parseObject(DffPayBase64.decode(ordeRsp.getBizStr()), new TypeReference<ResponseBody<OrderQueryRsp>>() {
                });
                if (!rspBody.isSuccess()) {
                    String respDesc = rspBody.getMsg();
                    channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                    if (StringUtils.hasText(respDesc)) {
                        channelPayOrderQueryContext.setRemark(respDesc);
                    }
                    else {
                        channelPayOrderQueryContext.setRemark("东方付提交支付订单查询返回错误信息为空,错误码为:" + rspBody.getCode());
                    }
                    return;
                }
                OrderQueryRsp orderQueryRsp = rspBody.getData();
                PayOrderDO updartePayOrderDO = new PayOrderDO();
                switch (orderQueryRsp.getStatus()) {
                    case "TRADE_SUCCESS":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                        break;
                    case "TRADE_CLOSED":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                        break;
                    case "TRADE_CREATE":
                    case "TRADE_TOBE_CONFIRMED":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                        break;
                    default:
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                }

                updartePayOrderDO.setInsOrderNo(orderQueryRsp.getOrderId());
                updartePayOrderDO.setChannelOrderNo(orderQueryRsp.getOrderno());
                channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
            }
            else {
                String respDesc = ordeRsp.getMsg();
                channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelPayOrderQueryContext.setRemark(respDesc);
                }
                else {
                    channelPayOrderQueryContext.setRemark("东方付提交支付订单查询返回错误信息为空,错误码为:" + ordeRsp.getCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("东方付查询支付订单请求失败 message:{}", e.getMessage(), e);
            channelPayOrderQueryContext.setRemark("东方付查询支付订单请求失败,失败信息" + e.getMessage());
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
        }
    }

    /**
     * 东方付关闭订单
     *
     * @param channelPayOrderQueryContext
     */
    public void commonCloseChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
    }

    /**
     * 东方付查询关闭订单状态
     *
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        // 东方付无此接口
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
        channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
    }

    /**
     * 东方付订单退款
     *
     * @param channelRefundPayOrderContext
     */
    public void commonRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelRefundPayOrderContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        DffPayConfig dffPayConfig = getPayConfig(channelConfig, channelRefundPayOrderContext.getMerchantAppChannelDO());

        PayOrderDO payOrderDO = channelRefundPayOrderContext.getPayOrderDO();
        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        try {

            OrderRefundReq orderQueryReq = new OrderRefundReq();
            orderQueryReq.setOrderId(payOrderDO.getInsOrderNo());
            orderQueryReq.setRefundId(refundOrderDO.getRefundOrderNo());
            orderQueryReq.setReason(refundOrderDO.getRefundReason());
            orderQueryReq.setRefundAmount(MerchantUtils.YuanToFen(refundOrderDO.getRefundAmount()));

            String bodyJson = JsonUtils.toJsonString(orderQueryReq);
            String body = Base64.getEncoder().encodeToString(bodyJson.getBytes(StandardCharsets.UTF_8));
            HttpRequest request = createRequest(body, dffPayConfig);

            log.info("东方付订单退款req参数:{}", JsonUtils.toJsonString(request));
            String url = domain(channelRefundPayOrderContext.isTest()) + DffPayConstant.REFUND_ORDER;
            String response = HttpUtils.post(url, request);
            log.info("东方付订单退款返回参数:{}", response);

            // 响应报文验签
            HttpResponse ordeRsp = JsonUtils.parseObject(response, HttpResponse.class);
            if ("200".equals(ordeRsp.getCode())) {
                ResponseBody<OrderRefundRsp> rspBody = JsonUtils.parseObject(DffPayBase64.decode(ordeRsp.getBizStr()), new TypeReference<ResponseBody<OrderRefundRsp>>() {
                });
                if (!rspBody.isSuccess()) {
                    String respDesc = rspBody.getMsg();
                    channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                    if (StringUtils.hasText(respDesc)) {
                        channelRefundPayOrderContext.setRemark(respDesc);
                    }
                    else {
                        channelRefundPayOrderContext.setRemark("东方付提交退款订单查询返回错误信息为空,错误码为:" + rspBody.getCode());
                    }
                    return;
                }
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                OrderRefundRsp orderRefundRsp = rspBody.getData();
                if (orderRefundRsp.getFundChange()) {
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                    updateRefundOrderDO.setSuccessTime(System.currentTimeMillis());
                } else
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());

                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = ordeRsp.getMsg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("东方付提交退款订单返回错误信息为空,错误码为:" + ordeRsp.getCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("东方付提交退款订单请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("东方付提交退款订单请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    /**
     * 东方付订单退款状态查询
     *
     * @param channelRefundPayOrderContext
     */
    public void commonQueryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelRefundPayOrderContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        DffPayConfig dffPayConfig = getPayConfig(channelConfig, channelRefundPayOrderContext.getMerchantAppChannelDO());

        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        try {

            OrderRefundQueryReq orderQueryReq = new OrderRefundQueryReq();
            orderQueryReq.setOrderId(refundOrderDO.getPayOrderNo());
            orderQueryReq.setRefundId(refundOrderDO.getRefundOrderNo());

            String bodyJson = JsonUtils.toJsonString(orderQueryReq);
            String body = Base64.getEncoder().encodeToString(bodyJson.getBytes(StandardCharsets.UTF_8));
            HttpRequest request = createRequest(body, dffPayConfig);

            log.info("东方付订单退款查询req参数:{}", JsonUtils.toJsonString(request));
            String url = domain(channelRefundPayOrderContext.isTest()) + DffPayConstant.REFUND_QUERY;
            String response = HttpUtils.post(url, request);
            log.info("东方付订单退款查询返回参数:{}", response);

            HttpResponse ordeRsp = JsonUtils.parseObject(response, HttpResponse.class);
            if ("200".equals(ordeRsp.getCode())) {
                ResponseBody<OrderRefundQueryRsp> rspBody = JsonUtils.parseObject(DffPayBase64.decode(ordeRsp.getBizStr()), new TypeReference<ResponseBody<OrderRefundQueryRsp>>() {
                });
                if (!rspBody.isSuccess()) {
                    String respDesc = rspBody.getMsg();
                    channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                    if (StringUtils.hasText(respDesc)) {
                        channelRefundPayOrderContext.setRemark(respDesc);
                    }
                    else {
                        channelRefundPayOrderContext.setRemark("东方付提交退款订单查询返回错误信息为空,错误码为:" + rspBody.getCode());
                    }
                    return;
                }
                OrderRefundQueryRsp refundQueryRsp = rspBody.getData();
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                switch (refundQueryRsp.getStatus()) {
                    case "REFUND_SUCCESS":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                        break;
                    case "REFUND_FAILED":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
                        break;
                    default:
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
                        break;
                }
                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = ordeRsp.getMsg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("东方付提交退款订单返回错误信息为空,错误码为:" + ordeRsp.getCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("东方付退款订单查询请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("东方付退款订单查询请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    public DffpayAppChannelParams dffPayAppChannelParams(MerchantAppChannelDO merchantAppChannelDO) {
        return JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), DffpayAppChannelParams.class);
    }

    public String domain(boolean test) {
        return DffPayConstant.PAY_ORDER_DOMAIN_PROD;
    }

    public Integer expireMinute(Long time) {
        if (time == null) {
            return 30;
        }
        return (int) (time - System.currentTimeMillis()) / 1000 / 60;
    }

    public DffPayConfig getPayConfig(String channelConfig, MerchantAppChannelDO merchantAppChannelDO) {
        // 配置转换
        DffPayConfig dffPayConfig = JsonUtils.parseObject(channelConfig, DffPayConfig.class);

        DffpayAppChannelParams dffpayAppChannelParams = dffPayAppChannelParams(merchantAppChannelDO);

        if (StringUtils.hasText(dffpayAppChannelParams.getMchPrivateKey()))
            dffPayConfig.setMchPrivateKey(dffpayAppChannelParams.getMchPrivateKey());

        if (StringUtils.hasText(dffpayAppChannelParams.getAppSecKey()))
            dffPayConfig.setAppSecKey(dffpayAppChannelParams.getAppSecKey());

        if (StringUtils.hasText(dffpayAppChannelParams.getPublicKey()))
            dffPayConfig.setPublicKey(dffpayAppChannelParams.getPublicKey());

        if (StringUtils.hasText(dffpayAppChannelParams.getSappId()))
            dffPayConfig.setSappId(dffpayAppChannelParams.getSappId());

        if (StringUtils.hasText(dffpayAppChannelParams.getSubMchId()))
            dffPayConfig.setSubMchId(dffpayAppChannelParams.getSubMchId());
        return dffPayConfig;
    }

    protected HttpRequest createRequest(String body, DffPayConfig dffPayConfig) throws Exception {
        HttpRequest paymentOrdeReq = new HttpRequest();
        paymentOrdeReq.setAppSecKey(dffPayConfig.getAppSecKey());
        paymentOrdeReq.setTimestamp(DateUtils.getDateString(DatePattern.NORM_DATETIME_PATTERN));
        paymentOrdeReq.setBizStr(body);
        paymentOrdeReq.setSign(DffPaySignUtil.sign(body, dffPayConfig.getMchPrivateKey()));
        return paymentOrdeReq;
    }

    public static void main(String[] args) {
        //AAAAAFFFFFFCCCCCC95218
        String orderId = "2c9b818f95696558019569863f410e42";
        String refundId = "AAAAAFFFFFF95218";
//        OrderQueryReq orderQueryReq = new OrderQueryReq();
//        orderQueryReq.setOrderId("2c9b818f95696558019569863f410e42");

//        OrderRefundReq orderQueryReq = new OrderRefundReq();
//        orderQueryReq.setOrderId(orderId);
//        orderQueryReq.setRefundId(refundId);
//        orderQueryReq.setReason("测试退款");
//        orderQueryReq.setRefundAmount(1);

        System.out.println(DffPayBase64.decode("eyJvcmRlcklkIjoiRDIwMjUwMzA2MTU1NzEyMjMzNTkyODMyIiwicmVmdW5kSWQiOiJUMjAyNTAzMDYxNzA1Mjg1NjU5MTAwMTYiLCJyZWFzb24iOiIxMjMxMjMiLCJyZWZ1bmRBbW91bnQiOjF9"));
    }
}
